﻿#pragma once
#include "df.h"
#include "ErrCode.h"



#if defined(__has_include)
#	if __has_include(<execinfo.h>)
#		define DF_HAS_execinfo
#	endif
#endif



#if defined _MSC_VER
#	ifdef UNICODE
#		define  DBGHELP_TRANSLATE_TCHAR
#	endif
#	include "codeconvert.h"
#	include <dbghelp.h>  
#	include <psapi.h> 
#	pragma comment( lib, "dbghelp" )  
#elif  defined  __ANDROID__
#	include <unwind.h>
#	include <dlfcn.h>
// #	include "libunwind/libunwind.h"
// #elif defined(__APPLE__)
// 
#elif defined(DF_HAS_execinfo)
#	include <execinfo.h>
#endif



namespace df
{
	using namespace sdf;

	//记录错误信息
	class Err
	{
	private:
		//当不为0时表示错误
		int errCode_ = 0;

		//错误信息
		String info_;

	public:
		Err()
		{

		}
		Err(const Err & r)
			: errCode_(r.errCode_)
			, info_(r.info_)
		{
		}

		Err(Err && r)
			: errCode_(r.errCode_)
			, info_(std::move(r.info_))
		{
		}

		Err(int err, CC info)
			: errCode_(err)
			, info_(info.c_str(), info.length())
		{

		}

		void Init()
		{
			errCode_ = 0;
			info_ = tt_("");
		}

		void Set(int err,const CC & info)
		{
			errCode_ = err;
			info_.assign(info.c_str(), info.size());
		}

		void Set(int err, String && info)
		{
			errCode_ = err;
			info_ = std::move(info);
		}

		void SetLastError(CC info)
		{
			errCode_ = (int)df::GetError();
			info_.assign(info.c_str(), info.size());

			if (errCode_ == 0) {
				errCode_ = 1;
			}
			else {
				info_.append(tt_("\r\n"));
				df::GetErrorString(info_);
			}

		}

		void SetLastError(String && info)
		{
			errCode_ = (int)df::GetError();
			info_ = std::move(info);

			if (errCode_ == 0) {
				errCode_ = df::ErrCode::UnknowLastError;
			}
			else {
				info_.append(tt_("\r\n"));
				df::GetErrorString(info_);
			}
		}

		int GetErrCode() const
		{
			return errCode_;
		}



		inline operator bool() const
		{
			return errCode_ != 0;
		}

		CC GetInfo() const
		{
			return info_;
		}





#if  defined  __ANDROID__



		struct BacktraceState
		{
			void** current;
			void** end;
			void* pc;
		};

		static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
		{
			BacktraceState* state = static_cast<BacktraceState*>(arg);
			uintptr_t pc = _Unwind_GetIP(context);
			if (pc) {
				if (state->current == state->end) {
					return _URC_END_OF_STACK;
				}
				else {
					*state->current++ = reinterpret_cast<void*>(pc);
				}
			}
			return _URC_NO_REASON;
		}


		static size_t captureBacktrace(void** buffer, size_t max)
		{
			BacktraceState state = { buffer, buffer + max };
			_Unwind_Backtrace(unwindCallback, &state);

			return state.current - buffer;
		}

		static void dumpBacktrace(String & os, void** buffer, size_t count)
		{
			for (size_t idx = 0; idx < count; ++idx) {
				const void* addr = buffer[idx];
				const char* symbol = "";

				if (idx > 0 && addr == buffer[idx - 1]) {
					continue;
				}

				Dl_info info;
				if (dladdr(addr, &info) && info.dli_sname) {
					symbol = info.dli_sname;
					os << (void*)((uintptr_t)addr - (uintptr_t)info.dli_fbase) << "\r\n" << symbol << "\r\n";
				}

			}
		}
		static void coffeecatch_start_alarm(void) {
			/* Ensure we do not deadlock. Default of ALRM is to die.
			* (signal() and alarm() are signal-safe) */
			(void)alarm(30);
		}

		typedef struct map_info_t map_info_t;

		struct backtrace_frame_t {
			uintptr_t absolute_pc;
			uintptr_t stack_top;
			size_t stack_size;
		};

		struct backtrace_symbol_t {
			uintptr_t relative_pc;
			uintptr_t relative_symbol_addr;
			char* map_name;
			char* symbol_name;
			char* demangled_name;
		};
		/* Extracted from Android's libcorkscrew/arch-arm/backtrace-arm.c */
		typedef ssize_t(*t_unwind_backtrace_signal_arch)
			(siginfo_t* si, void* sc, const map_info_t* lst, backtrace_frame_t* bt,
				size_t ignore_depth, size_t max_depth);
		typedef map_info_t* (*t_acquire_my_map_info_list)();
		typedef void(*t_release_my_map_info_list)(map_info_t* milist);
		typedef void(*t_get_backtrace_symbols)(const backtrace_frame_t* backtrace,
			size_t frames,
			backtrace_symbol_t* symbols);
		typedef void(*t_free_backtrace_symbols)(backtrace_symbol_t* symbols,
			size_t frames);


		static const int BACKTRACE_FRAMES_MAX = 32;
		/* Thread-specific crash handler structure. */
		struct native_code_handler_struct {



			/* Signal code and info. */
			int code;
			siginfo_t si;
			ucontext_t uc;

			/* Uwind context. */
			backtrace_frame_t frames[BACKTRACE_FRAMES_MAX];
			void* uframes[BACKTRACE_FRAMES_MAX];
			size_t frames_size;
			size_t frames_skip;

		};


		/* Use libcorkscrew to get a backtrace inside a signal handler.
		Will only return a non-zero code on Android >= 4 (with libcorkscrew.so
		being shipped) */
		static size_t coffeecatch_backtrace_signal(siginfo_t* si, void* sc,
			backtrace_frame_t* frames,
			size_t ignore_depth,
			size_t max_depth) {
			void *const libcorkscrew = dlopen("libcorkscrew.so", RTLD_LAZY | RTLD_LOCAL);
			if (libcorkscrew != NULL) {
				t_unwind_backtrace_signal_arch unwind_backtrace_signal_arch
					= (t_unwind_backtrace_signal_arch)
					dlsym(libcorkscrew, "unwind_backtrace_signal_arch");
				t_acquire_my_map_info_list acquire_my_map_info_list
					= (t_acquire_my_map_info_list)
					dlsym(libcorkscrew, "acquire_my_map_info_list");
				t_release_my_map_info_list release_my_map_info_list
					= (t_release_my_map_info_list)
					dlsym(libcorkscrew, "release_my_map_info_list");
				if (unwind_backtrace_signal_arch != NULL
					&& acquire_my_map_info_list != NULL
					&& release_my_map_info_list != NULL) {
					map_info_t*const info = acquire_my_map_info_list();
					const ssize_t size =
						unwind_backtrace_signal_arch(si, sc, info, frames, ignore_depth,
							max_depth);
					release_my_map_info_list(info);

					dlclose(libcorkscrew);
					return size >= 0 ? size : 0;
				}
				else {
					//DEBUG(print("symbols not found in libcorkscrew.so\n"));
				}
				dlclose(libcorkscrew);
			}
			else {
				//DEBUG(print("libcorkscrew.so could not be loaded\n"));
			}
			return 0;
		}
// 
// 		typedef struct unw_cursor
// 		{
// 			unw_word_t opaque[UNW_TDEP_CURSOR_LEN];
// 		}
// 		unw_cursor_t;
// 
// 		typedef enum
// 		{
// 			UNW_REG_IP = UNW_TDEP_IP,		/* (rw) instruction pointer (pc) */
// 			UNW_REG_SP = UNW_TDEP_SP,		/* (ro) stack pointer */
// 			UNW_REG_EH = UNW_TDEP_EH,		/* (rw) exception-handling reg base */
// 			UNW_REG_LAST = UNW_TDEP_LAST_REG
// 		}
// 		unw_frame_regnum_t;

		/* Use libunwind to get a backtrace inside a signal handler.
		Will only return a non-zero code on Android >= 5 (with libunwind.so
		being shipped) */
// 		static ssize_t coffeecatch_unwind_signal(siginfo_t* si, void* sc,
// 			backtrace_frame_t* frames,
// 			size_t ignore_depth,
// 			size_t max_depth) {
// 			void *libunwind = dlopen("libunwind.so", RTLD_LAZY | RTLD_LOCAL);
// 			if (libunwind != NULL) {
// 				int(*unw_init_local)(void *cursor, void *context) = (int(*)(void *cursor, void *context))dlsym(libunwind, "_ULarm_init_local");
// 				int(*unw_step)(void *cursor) = (int(*)(void *cursor))dlsym(libunwind, "_ULarm_step");
// 				int(*unw_get_reg)(void *cursor, int, unw_word_t *pc) = (int(*)(void *cursor, int, uintptr_t *pc))dlsym(libunwind, "_ULarm_get_reg");
// 				if (unw_init_local != NULL && unw_get_reg != NULL && unw_step != NULL) {
// 					unw_cursor_t    cursor;
// 					unw_init_local(&cursor, sc);
// 
// 					int i = 0;
// 					while (unw_step(&cursor) > 0) {
// 						unw_word_t   pc;
// 						unw_get_reg(&cursor, UNW_REG_IP, &pc);
// 						frames[i].absolute_pc = pc;
// 						i++;
// 
// 
// 
// 						if (i >= max_depth)
// 							break;
// 					}
// 					dlclose(libunwind);
// 					return i;
// 				}
// 				else {
// 					df::WriteLog("symbols not found in libunwind.so", false);
// 					//DEBUG(print("symbols not found in libunwind.so\n"));
// 				}
// 				dlclose(libunwind);
// 			}
// 			else {
// 				df::WriteLog("libunwind.so could not be loaded", false);
// 				//DEBUG(print("libunwind.so could not be loaded\n"));
// 			}
// 			return 0;
// 		}

		/* Copy context infos (signal code, etc.) */
		static void coffeecatch_copy_context(native_code_handler_struct *const t,
			const int code, siginfo_t *const si,
			void *const sc) {
			t->code = code;
			t->si = *si;
			if (sc != NULL) {
				ucontext_t *const uc = (ucontext_t*)sc;
				t->uc = *uc;
			}
			else {
				memset(&t->uc, 0, sizeof(t->uc));
			}


			/* Use the corkscrew library to extract the backtrace. */
			// 			if (t->frames_size == 0) {
			// 				t->frames_size = coffeecatch_unwind_signal(si, sc, t->frames, 0,
			// 					BACKTRACE_FRAMES_MAX);
			// 			}

			if (t->frames_size == 0) {
				t->frames_size = coffeecatch_backtrace_signal(si, sc, t->frames, 0,
					BACKTRACE_FRAMES_MAX);
			}



			if (t->frames_size != 0) {
				//DEBUG(print("called _Unwind_Backtrace()\n"));
			}
			else {
				//df::WriteLog("called _Unwind_Backtrace(), but no traces", false);
				//DEBUG(print("called _Unwind_Backtrace(), but no traces\n"));
			}
		}

		static void print_call_link(String & os, int sig_no, siginfo_t *pt_siginfo, void *p_ucontext)
		{
			native_code_handler_struct t = { 0 };

			/* Take note of the signal. */
			coffeecatch_copy_context(&t, sig_no, pt_siginfo, p_ucontext);
			for (size_t idx = 0; idx < t.frames_size; ++idx) {

				const void* addr = (void*)t.frames[idx].absolute_pc;
				const char* symbol = "";

				//if (idx > 0 && addr == buffer[idx - 1]) {
				//	continue;
				//}

				Dl_info info;
				if (dladdr(addr, &info) && info.dli_sname) {
					symbol = info.dli_sname;
					os << (void*)((uintptr_t)addr - (uintptr_t)info.dli_fbase) << "\r\n" << symbol << "\r\n";
				}

			}

	}
#elif defined _MSC_VER


		static inline void  addressToString(HANDLE hProcess, PVOID address, String & oss)
		{
			// Then any name for the symbol  
			struct tagSymInfo
			{
				IMAGEHLP_SYMBOL64 symInfo;
				char nameBuffer[4 * 256];
			} SymInfo = { { sizeof(IMAGEHLP_SYMBOL64) } };
			IMAGEHLP_SYMBOL64 * pSym = &SymInfo.symInfo;
			pSym->MaxNameLength = sizeof(SymInfo) - offsetof(tagSymInfo, symInfo.Name);
			DWORD64 dwDisplacement = 0;
			DWORD dwDisplacement2 = 0;
			if (SymGetSymFromAddr64(hProcess, (DWORD_PTR)address, &dwDisplacement, pSym))
			{
#ifdef UNICODE
				df::code::AnsiToWide(pSym->Name, oss);
#else
				oss << pSym->Name;
#endif

				// 				if (dwDisplacement != 0)
				// 					oss << dwDisplacement;
		}

			// Finally any file/line number  
			IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };

			if (SymGetLineFromAddr64(hProcess, (DWORD_PTR)address, &dwDisplacement2, &lineInfo))
			{
				//char const *pDelim = strrchr(lineInfo.FileName, '//');
				oss << tcc_(" - ") << df::CC(lineInfo.FileName) << tcc_("(") << lineInfo.LineNumber << tcc_(")");
			}
}

		static inline void StackTrace(PCONTEXT c, String & os)
		{
			HANDLE hProcess = ::GetCurrentProcess();
			DWORD dwOpts = ::SymGetOptions();
			dwOpts |= SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS;
			::SymSetOptions(dwOpts);
			::SymInitialize(hProcess, 0, true);
			ON_SCOPE_EXIT({
				::SymCleanup(hProcess);
			});



			STACKFRAME64 sf = { 0 };
			// 不同的CPU类型，具体信息可查询MSDN  
			DWORD dwImageType = IMAGE_FILE_MACHINE_I386;
#ifdef _M_IX86  
			sf.AddrPC.Offset = c->Eip;
			sf.AddrPC.Mode = AddrModeFlat;
			sf.AddrStack.Offset = c->Esp;
			sf.AddrStack.Mode = AddrModeFlat;
			sf.AddrFrame.Offset = c->Ebp;
			sf.AddrFrame.Mode = AddrModeFlat;
#elif _M_X64  
			dwImageType = IMAGE_FILE_MACHINE_AMD64;
			sf.AddrPC.Offset = c->Rip;
			sf.AddrPC.Mode = AddrModeFlat;
			sf.AddrFrame.Offset = c->Rsp;
			sf.AddrFrame.Mode = AddrModeFlat;
			sf.AddrStack.Offset = c->Rsp;
			sf.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64  
			dwImageType = IMAGE_FILE_MACHINE_IA64;
			sf.AddrPC.Offset = c->StIIP;
			sf.AddrPC.Mode = AddrModeFlat;
			sf.AddrFrame.Offset = c->IntSp;
			sf.AddrFrame.Mode = AddrModeFlat;
			sf.AddrBStore.Offset = c->RsBSP;
			sf.AddrBStore.Mode = AddrModeFlat;
			sf.AddrStack.Offset = c->IntSp;
			sf.AddrStack.Mode = AddrModeFlat;
#else  
#error "Platform not supported!"  
#endif  

			while (::StackWalk64(
				dwImageType,
				hProcess,
				GetCurrentThread(), // this value doesn't matter much if previous one is a real handle  
				&sf,
				c,
				NULL,
				::SymFunctionTableAccess64,
				::SymGetModuleBase64,
				NULL))
			{
				os << (PVOID)sf.AddrFrame.Offset << tcc_(" ");
				addressToString(hProcess, (PVOID)sf.AddrPC.Offset, os);
				os << tcc_("\r\n");
			}
		}


#endif


		//记录调用栈
		static FORCEINLINE void BackTrace(String & str)
		{
#if defined _MSC_VER
			CONTEXT cont = { 0 };
			cont.ContextFlags = CONTEXT_FULL;
			if (GetThreadContext(GetCurrentThread(), &cont))
				StackTrace(&cont, str);
#elif  defined  __ANDROID__
			const size_t max = 30;
			void* buffer[max];
			dumpBacktrace(str, buffer, captureBacktrace(buffer, max));
			// #elif defined __APPLE__

#elif defined DF_HAS_execinfo
			int j, nptrs;
			void *buffer[100];
			char **strings;

			nptrs = backtrace(buffer, 100);
			strings = backtrace_symbols(buffer, nptrs);
			ON_SCOPE_EXIT({
				free(strings);
			});

			if (strings != NULL)
			{
				for (j = 0; j < nptrs; j++)
					str << df::CC(strings[j]) << tcc_("\r\n");
		}
#endif
		}

	};


	class ErrExpand
	{
	public:
		static void Construct(Err * er)
		{
			if (er)
				er->Init();
		}

		static void Destruct(Err *)
		{

		}


	};

	//用来返回错误信息
	using ErrWrap = PtrWrap<Err, ErrExpand>;

}
